Using Github Action to Automatically Deploy Websites/Backend Applications
As the number of projects I write increases, so too does the demand for deployment.
Previously, the method I used to update deployments was to write a Bash script to semi-automatically update the project:
Whenever the project had new changes completed, for front-end projects, the build script would run automatically, and then upload to the remote server (which has been configured with Nginx settings) using the scp tool. For backend projects, the application would be automatically containerized, and then I would still need to ssh to the server background to manually run the docker-compose.yml file to pull and run the new image.
In short, every time new changes were pushed to the GitHub repository, I had to manually run this Bash script. Finally, I recently decided to fully automate this process!
Because I have been using Vercel to deploy ChatGPT-Next-Web as my AI chat assistant recently, I found that every time a new commit was submitted to this repository, my Vercel application would automatically update. On this occasion, I learned about Github Action.
After some exploration and failures, I finally succeeded in setting all my projects to fully automated deployment with Github Action.
Here, I will record a typical Workflow setting for future reference:
name: CI/CD Pipeline
on:
push:
branches:
- main
jobs:
check-skip-ci:
runs-on: ubuntu-latest
outputs:
skip: ${{ steps.check.outputs.skip }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check for [skip ci] in commit message
id: check
run: |
if [[ "${{ github.event.head_commit.message }}" == *"[skip ci]"* ]]; then
echo "::set-output name=skip::true"
else
echo "::set-output name=skip::false"
fi
build-and-deploy:
needs: check-skip-ci
if: needs.check-skip-ci.outputs.skip == 'false'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20.12.2"
- name: Install TypeScript
run: npm install -g typescript@4.9.3
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Build the project
run: pnpm run build
- name: Log in to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
tags: ${{ secrets.DOCKER_USERNAME }}/bookkeeper:latest
push: true
- name: Deploy to remote server
env:
HOST: ${{ secrets.REMOTE_HOST }}
USER: ${{ secrets.REMOTE_USER }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
echo "$SSH_PRIVATE_KEY" > private_key
chmod 600 private_key
ssh -i private_key -o StrictHostKeyChecking=no $USER@$HOST 'docker compose -f /root/www/bkp/docker-compose.yml down || true && sudo rm -f /root/www/bkp/docker-compose.yml'
scp -i private_key -o StrictHostKeyChecking=no docker-compose.yml $USER@$HOST:/root/www/bkp/docker-compose.yml
ssh -i private_key -o StrictHostKeyChecking=no $USER@$HOST <<EOF
docker compose -f /root/www/bkp/docker-compose.yml pull
docker compose -f /root/www/bkp/docker-compose.yml up -d
docker system prune -f
EOF
rm -rf private_key
In this settings file, I added two jobs, one is check-skip-ci
, and the other is build-and-deploy
.
The purpose of check-skip-ci
is to check the commit message for the phrase [skip ci]
when a new commit is pushed to the Github repository. If it is found, it skips build-and-deploy
. The purpose of writing this job is that there may be times when I just make changes to the README.md file, and in these cases, I wouldn’t want my project to be rebuilt and redeployed. I can add [skip ci]
at the beginning of the commit message to skip this process.
Finally, the method and location for adding environment variables are shown in the figure.